<?php
/**
 * @license http://www.gnu.org/licenses/lgpl.html LGPL
 * @author Harlan Iverson <h dot iverson at gmail dot com>
 * @copyright Copyright (c) 2006, Harlan Iverson.
 * @version $Id: ViewHandler.class.php 21 2006-08-04 05:02:34Z Harlan $
 */


define( "FaceletsTemplateResource_VARIABLE_PATTERN", "/(\#\{([^\}]+)\})/" );
define( "FaceletsTemplateResource_HACKED_VARIABLE_PATTERN", "/(\#\~([^\~]+)\~)/" );


/**
 * The Facelets to Smarty converter
 */
class PhpFaces_FaceletsTemplateResource {
	
	/**
	 * @param &$f2sXml XmlReader the XML reader to parse from
	 * @param $shouldResolveExpressions boolean weather to resolve the attribute value before returning it, or return it as-is.
	 * @return string a string containing attributes for given elements
	 */
	protected function parseAttributes( XmlReader &$f2sXml, $shouldResolveExpressions = false ) {
		$output = "";
		if( $f2sXml->hasAttributes ) {
			
			while( $f2sXml->moveToNextAttribute() ) {
				$value = $shouldResolveExpressions ? preg_replace( FaceletsTemplateResource_VARIABLE_PATTERN, '{"$1"|faces_el}', $f2sXml->value ) : $f2sXml->value;
				$output .= ' '.$f2sXml->name . '="'.$value.'"';
			}
			
			$f2sXml->moveToElement();
		}
		
		//echo "--- attributes (elem=".$f2sXml->name.", should have ".$f2sXml->attributeCount." attrs): $output ---\n";
		
		return $output;
	}
	
	
	
	/**
	 * The main loop that parses a Facelets file to a Smarty template
	 * @param $tpl_source string the original facelets source
	 * @param &$smarty Smarty a reference to the current instance of smarty
	 * @return string The parsed, pure-smarty template
	 * @todo make this method modular!
	 */
	public static function &ParseFacelets($tpl_source, &$smarty)
	{
		
		$isThisAComposition = ($smarty->get_template_vars( "composition" ) == "true");
		
		$smartyTemplate = "";
		$isDefineSection = false;
		$isIncludeSection = false;
		$isRepeatSection = false;
		$isOutputLabel = false;
		$isOutputDataTable = false;
		$isOutputColumn = false;
		
		$f2sXml = new XmlReader();
		$f2sXml->XML( $tpl_source );
		$f2sXml->setParserProperty(2,true); // This seems a little unclear to me - but it worked :)
		
		
		
		while( $f2sXml->read() ) {
		
			if( $f2sXml->hasAttributes && $jsfc = $f2sXml->getAttribute("jsfc") ) {
				
				$jsfc = split( ":", $jsfc );
				$element = $jsfc[1];
				$namespace = $f2sXml->lookupNamespace( $jsfc[0] );
			} else {
				$element = $f2sXml->localName;
				$namespace = $f2sXml->namespaceURI;
			}
			
			
			
			switch( $namespace ) {
				case "http://java.sun.com/jsf/facelets":;
					switch ( $element ) {
						case "component":
							break;
						case "composition":
							if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$smartyTemplate .= '{include file="'.$f2sXml->getAttribute("template").'" composition="true"}'."\n";
							}
							break;
						case "debug":
							break;
						case "decorate":
							break;
						case "define":
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{capture assign="'.$f2sXml->getAttribute("name").'"}';
								$isDefineSection = true;
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$smartyTemplate .= '{/capture}';
								$isDefineSection = false;
							}
							break;
						case "include":
							if( $f2sXml->isEmptyElement ) {
								$smartyTemplate .= '{include file=""'.$f2sXml->getAttribute("src").'"}'."\n";
							} else if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$isIncludeSection = true;
								$smartyTemplate .= '{include file=""'.$f2sXml->getAttribute("src").'"';
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$isIncludeSection = false;
								$smartyTemplate .= '}'."\n";
							}
							break;
						case "insert":
							if( $f2sXml->isEmptyElement ) {
								$smartyTemplate .= '{$'.$f2sXml->getAttribute("name").'}'."\n";
							} else if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{$'.$f2sXml->getAttribute("name");
								$isInsert = true;
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$isInsert = false;
								$smartyTemplate .= '}'."\n";
							}
							break;
						case "param":
							if( $isIncludeSection && $f2sXml->hasAttributes ) {
								$smartyTemplate .= ' '.$f2sXml->getAttribute("name").'="'.$f2sXml->getAttribute("value").'"|faces_el';
								
								// move on to the end of the element
								$endOfAttributeIndex = $f2sXml->attributeCount - 1;
								$f2sXml->moveToAttributeNo( $endOfAttributeIndex );
							}
							break;
						case "remove":
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{* ';
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$smartyTemplate .= ' *}'."\n";
							}
							break;
						case "repeat":
						 	if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{foreach from="'.$f2sXml->getAttribute("value").'"|faces_el item="'.$f2sXml->getAttribute("var").'"}'."\n";
								$smartyTemplate .= '{faces_addToContext name="'.$f2sXml->getAttribute("var").'" value=$'.$f2sXml->getAttribute("var").'}'."\n";
								$isRepeatSection = true;
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$smartyTemplate .= '{faces_removeFromContext name="'.$f2sXml->getAttribute("var").'"}'."\n";
								$smartyTemplate .= '{/foreach}'."\n";
								$isRepeatSection = false;
							}
						 
							break;
		
					}
					break;
		
				case "http://java.sun.com/jsf/html":

					switch ( $element ) {
						case "inputText":
							if( $f2sXml->nodeType == XMLReader::ELEMENT || $f2sXml->isEmptyElement ) {
								$smartyTemplate .= '{faces_inputText'.self::parseAttributes( $f2sXml ).'}{/faces_inputText}';
							}
							break;
						case "inputTextarea":
							if( $f2sXml->nodeType == XMLReader::ELEMENT || $f2sXml->isEmptyElement ) {
								$smartyTemplate .= '{faces_inputTextarea'.self::parseAttributes( $f2sXml ).'}{/faces_inputTextarea}';
							}
							break;
						case "commandButton":
							if( $f2sXml->nodeType == XMLReader::ELEMENT || $f2sXml->isEmptyElement ) {
								$smartyTemplate .= '{faces_commandButton'.self::parseAttributes( $f2sXml ).'}{/faces_commandButton}';
							}
							break;
						case "outputLabel":
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{faces_outputLabel'.self::parseAttributes( $f2sXml ).'}';
								$isOutputLabel = true;
							}

							if( $f2sXml->nodeType == XMLReader::END_ELEMENT || $f2sXml->isEmptyElement) {
								$isOutputLabel = false;
								$smartyTemplate .= '{/faces_outputLabel}';
							}
							break;
						case "form":
							
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '<form method="POST">'."\n";
							} else if( $f2sXml->nodeType == XMLReader::END_ELEMENT ) {
								$smartyTemplate .= '</form>'."\n";
							}
							break;
						case "dataTable":
							
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{faces_dataTable'.self::parseAttributes( $f2sXml ).'}';
								/*
								$smartyTemplate .= '<table>'."\n";
								$smartyTemplate .= '{foreach from="'.$f2sXml->getAttribute("value").'"|faces_el item="'.$f2sXml->getAttribute("var").'"}'."\n";
								$smartyTemplate .= '<tr>'."\n";
								$smartyTemplate .= '{faces_addToContext name="'.$f2sXml->getAttribute("var").'" value=$'.$f2sXml->getAttribute("var").'}'."\n";
								*/
								$isOutputDataTable = true;
							}

							if( $f2sXml->nodeType == XMLReader::END_ELEMENT || $f2sXml->isEmptyElement) {
								$isOutputDataTable = false;
								/*
								$smartyTemplate .= '{faces_removeFromContext name="'.$f2sXml->getAttribute("var").'"}'."\n";
								$smartyTemplate .= '</tr>'."\n";
								$smartyTemplate .= '{/foreach}'."\n";
								$smartyTemplate .= '</table>'."\n";
								*/
								$smartyTemplate .= '{/faces_dataTable}';
							}
							break;
						case "column":
							
							if( $f2sXml->nodeType == XMLReader::ELEMENT ) {
								$smartyTemplate .= '{faces_column'.self::parseAttributes( $f2sXml ).'}';
								$isOutputColumn = true;
							}

							if( $f2sXml->nodeType == XMLReader::END_ELEMENT || $f2sXml->isEmptyElement) {
								$isOutputColumn = false;
								$smartyTemplate .= '{/faces_column}';
							}
							break;
					}
					break;
		
				default:
					
					if( $isDefineSection || $isRepeatSection || $isThisAComposition || $isOutputLabel /* || $isOutputColumn || $isOutputDataTable */ ) {
						switch( $f2sXml->nodeType ) {
							case XMLReader::ELEMENT:
								$smartyTemplate .= '<'.$f2sXml->name;
								
								// parse attributes as we find them, except we will
								// resolve any faces expressions we come across.
								$smartyTemplate .= self::parseAttributes( $f2sXml, true );
								
								if( $f2sXml->isEmptyElement ) {
									$smartyTemplate .= '/';
								}
								$smartyTemplate .= '>'."\n";
								break;
							case XmlReader::END_ELEMENT:
								$smartyTemplate .= '</'.$f2sXml->name.'>'."\n";
								break;
							case XMLReader::TEXT:
							case XMLReader::SIGNIFICANT_WHITESPACE:
							case XMLReader::CDATA:
								//$smartyTemplate .= FaceletsTemplateResource::ParseEL( $f2sXml->value, $x = null );
								//$smartyTemplate .= FaceletsTemplateResource::ParseEL( $f2sXml->value, $x = null );
								
								$smartyTemplate .= preg_replace( FaceletsTemplateResource_VARIABLE_PATTERN, '{"$1"|faces_el}', $f2sXml->value );
								break;
							case XMLReader::COMMENT:
								$smartyTemplate .= '{* '.$f2sXml->value.' *}'."\n";
								break;
						}
					}
			}
		}
		
		
		$f2sXml->close();

		return $smartyTemplate;
	}
	
	/**
	 * I don't thinkg this is even used!
	 * @deprecated 13 - Jul 30, 2006
	 */
	public static function &ParseEL($tpl_source, &$smarty)
	{
		$tpl_source = preg_replace( FaceletsTemplateResource_VARIABLE_PATTERN, '{$facesContext->resolve("$1")}', $tpl_source );
		return $tpl_source;
	}
	
	/**
	 * Register Facelets on a given instance or smarty...
	 * facelets:// resource, pre-post filters, and default
	 * components including Core and HtmlWidgets
	 * @param &$smarty Smarty the insance of smarty to register on
	 */
	public static function register( &$smarty ) {
		$functionList = array( 
			array(__CLASS__, 'getTemplate'),
			array(__CLASS__, 'getTimestamp'),
			array(__CLASS__, 'isSecure'),
			array(__CLASS__, 'isTrusted') );

		$smarty->register_resource( "facelets", $functionList );
		
		// these filters keep smarty from choking on the facelets close
		// delimiter. really smarty should just parse better :)
		$smarty->register_prefilter( array( __CLASS__, "hack_prefilter" ) );
		$smarty->register_postfilter( array( __CLASS__, "hack_postfilter" ) );
		
		PhpFaces_Smarty_Core::register( $smarty );	
		PhpFaces_Smarty_HtmlWidgets::register( $smarty );
	}
	
	/**
	 * Replace all Faces EL expressions, #{expression}, with hacked #~expression~ ...
	 * There are known problems with this, but it works, generally.
	 */
	function hack_prefilter( $source, &$smarty ) {
		$source = preg_replace( FaceletsTemplateResource_VARIABLE_PATTERN, '#~$2~', $source );

		return $source;
	}
	
	/**
	 * Replace all hacked Faces EL expressions, #~expression~, with real #{expression} ...
	 * There are known problems with this, but it works, generally.
	 */
	function hack_postfilter( $source, &$smarty ) {
		$source = preg_replace( FaceletsTemplateResource_HACKED_VARIABLE_PATTERN, '#{$2}', $source );
		return $source;
	}

	/**
	 * Resolve a faelets resource path to a filesystem path
	 * @param &$smarty_obj Smarty a reference to the current instance
	 * @param $tpl_name string the path to the requested template
	 */
	function getResourcePath( &$smarty_obj, $tpl_name ) {
		return $smarty_obj->template_dir . "/" . $tpl_name;
	}
	
	/* ================================================
	 *       Implementing Smarty Resource Stream
	 *  http://smarty.php.net/manual/en/template.resources.php
	 *  http://smarty.php.net/manual/en/plugins.resources.php
	 * ================================================*/
	 
	/**
	 * Get a Facelets template
	 * @param $tpl_name string the file location of the template
	 * @param &$tpl_source string a reference to the bugger to place the source into
	 * @param &$smarty_obj Smarty a reference to the smarty object
	 * @return boolean true on success, false on failure
	 */
	function getTemplate ($tpl_name, &$tpl_source, &$smarty_obj)
	{
		$path = self::getResourcePath( $smarty_obj, $tpl_name );

	    if( file_exists( $path ) ) { //
	        $tpl_source = file_get_contents( $path ); //

	        $tpl_source = self::ParseFacelets( $tpl_source, $smarty_obj );
	        //echo "parsed: --- $tpl_source ---\n";
	        return true;;
	    } else { //
	        return false;
	    }}
	
	/**
	 * Get the timestamp of a facelets template
	 * @param $tpl_name string the file location of the template
	 * @param &$tpl_timestamp int a reference to the last modified timestamp
	 * @param &$smarty_obj Smarty a reference to the smarty object
	 * @return boolean true on success, false on failure
	 */
	function getTimestamp($tpl_name, &$tpl_timestamp, &$smarty_obj)
	{
	    $path = self::getResourcePath( $smarty_obj, $tpl_name );
	    
	    if ( file_exists( $path ) ) {
	        $tpl_timestamp = filemtime($path);
	        //$tpl_timestamp = time();
	        return true;;
	    } else { //
	        return false;;
	    } //
	}

	/**
	 * Assume all template are secure
	 * @param $tpl_name string the file location of the template
	 * @param &$smarty_obj Smarty a reference to the smarty object
	 * @return boolean true on secure, false on insecure
	 */
	function isSecure($tpl_name, &$smarty_obj)
	{
	    // assume all templates are secure
	    return true;
	}
	
	/**
	 * Not used for templates
	 * @param $tpl_name string the file location of the template
	 * @param &$smarty_obj Smarty a reference to the smarty object
	 * @return boolean true on success, false on failure
	 */
	function istTrusted($tpl_name, &$smarty_obj)
	{
	    // not used for templates
	}
	/* ================================================
	 *      End resource stream implementation
	 * ================================================
	 */

};


/**
 * 
 */
class PhpFaces_Smarty_Core {
	static function addToContext( $params ) {
		
		$name = $params["name"];
		$value = $params["value"];
		
		$facesContext =& PhpFaces_FacesContext::getCurrentInstance();
		
		$localContext =& $facesContext->localContext;
		$localContext[ $name ] =& $value;
	}
	static function removeFromContext( $params ) {
		
		$name = $params["name"];
		
		$facesContext =& PhpFaces_FacesContext::getCurrentInstance();
		
		$localContext =& $facesContext->localContext;
		unset( $localContext[ $name ] );
	}
	
	
	static function modifier_faces_el( $expression ) {
		$fc = PhpFaces_FacesContext::currentInstance();
		return $fc->resolve( $expression );
	}
	
	static function register( &$smarty ) {
		$smarty->register_modifier("faces_el", array(__CLASS__,"modifier_faces_el") );
		$smarty->register_function('faces_addToContext', array(__CLASS__,'addToContext') );
		$smarty->register_function('faces_removeFromContext', array(__CLASS__,'removeFromContext') );
	}
}

class PhpFaces_UIInputStruct {
	var $isRequired;
	var $styleClass;
	var $value;
	var $name;
	var $id;
	var $extraAttrs;
}

class PhpFaces_Smarty_HtmlWidgets {
	
	static function register( &$smarty ) {
		$smarty->register_function('faces_radio', array( __CLASS__, 'radio') );
		$smarty->register_block('faces_inputText', array( __CLASS__, 'inputText') );
		$smarty->register_block('faces_inputTextarea', array( __CLASS__, 'inputTextarea') );
		$smarty->register_block('faces_commandButton', array( __CLASS__, 'commandButton') );
		$smarty->register_block('faces_outputLabel', array( __CLASS__, 'outputLabel') );
		$smarty->register_block('faces_dataTable', array( __CLASS__, 'dataTable') );
		$smarty->register_block('faces_column', array( __CLASS__, 'column') );
	}
	
	static function &UIInput( &$params ) {
		$uiInput = new PhpFaces_UIInputStruct();
	
		$uiInput->isRequired = isset( $params["required"] ) && $params["required"] == "true" ? true : false;
		$uiInput->styleClass = isset( $params["styleClass"] ) ? $params["styleClass"] : null;
		$uiInput->id = isset( $params["id"] ) ? $params["id"] : null;
		
		// TODO validation
		$valueProperty = $params["value"];
		
		//echo "--- UIInput $valueProperty\n";
		
		$facesContext =& PhpFaces_FacesContext::getCurrentInstance();
		
		$uiInput->value = $facesContext->resolve( $valueProperty ); 
		$uiInput->name = $facesContext->getRequestName( $valueProperty );
	
		if( $uiInput->isRequired ) {
			// TODO this is a major hack, but it works for now
			// since nothing can ever have a hyphen in the name anyway.
			// since external files aren't affected by changes, it's 
			// fine for now
			$uiInput->name .= "-required";	
		}
		
		$uiInput->extraAttrs = array();
		if( $uiInput->styleClass != null ) {
			$uiInput->extraAttrs[] = 'class="'.$uiInput->styleClass.'"';
		}
		if( $uiInput->id != null ) {
			$uiInput->extraAttrs[] = 'id="'.$uiInput->id.'"';
		}
	
		return $uiInput;
	}
	

	static function inputTextarea( $params, $content, &$smarty, &$repeat ) {
		if( $repeat == false ) {
			if( isset( $content ) ) {
				// handle content	
			}
			
			$uiInput =& self::UIInput( $params );
			
			self::registerComponent( $params["id"], $params["value"] );
			
			$html = '<textarea name="'.$uiInput->name.'" '.join(' ', $uiInput->extraAttrs).'>'.htmlentities($uiInput->value).'</textarea>';
			return $html;
		}	
	}
	
	static function inputText( $params, $content, &$smarty, &$repeat ) {
		if( $repeat == false ) {
			if( isset( $content ) ) {
				// handle content	
			}
			
			$uiInput =& self::UIInput( $params );
			
			$size = isset($params["size"])?$params["size"]:null;
			if( $size != null ) {
				$uiInput->extraAttrs[] = "size=\"$size\"";	
			}
			
			self::registerComponent( $params["id"], $params["value"] );
			
			$html = '<input type="text" name="'.$uiInput->name.'" value="'.htmlentities($uiInput->value).'" '.join(' ', $uiInput->extraAttrs).'/>';
			return $html;
		}		
	}
	
	static function outputLabel ( $params, $content, &$smraty, &$repeat ){
		if( $repeat == false ) {
			if( isset( $content ) ) {
				// handle content	
			}
			
			$fc = PhpFaces_FacesContext::currentInstance();
			
			$for = $params["for"];
			
			$html = '<label for="'.htmlentities($for).'">';
			$html .= $content;
			$html .= '</label>'."\n";
			
			return $html;
		}	
	}
	
	static function commandButton( $params, $content, &$smarty, &$repeat ) {
		if( $repeat == false ) {
			if( isset( $content ) ) {
				// handle content	
			}

			$fc = PhpFaces_FacesContext::currentInstance();
			
			$action = $params["action"];
			$value = $params["value"];
			
			self::registerComponent( $params["id"], $action );
			
			$html = '<input type="submit" name="'.$fc->getActionName($action).'"'.($value?' value="'.htmlentities($value).'"':'').'/>';
			return $html;
		}		
	}
	
	/**
	 * @todo register these all in a session variable or output them with a form...
	 * use this map in "assign values" phase rather than just decoding an ID that
	 * is the EL expression.
	 */
	private static function registerComponent( $id, $el ) {
		if( !$id ) { $id = md5( microtime(true).rand(0,100000) ); }
		//echo "component [$id] - $el\n";
	}
	
	private static $dataTableStack = array();
	static function dataTable( $params, $content, &$smarty, &$repeat ) {
		if( $repeat ) {
			$table = new HtmlDataTable();
			$rows = $params["value"];
			$table->setRows( $rows );
			
			array_push( self::$dataTableStack, $table );
			//$html = "<table>\n";
			$html = "";
		} else {
			$table = array_pop( self::$dataTableStack );
			
			$html = "";
			//$html .= "</table>\n";	
		}
		return $html;
	}
	
	private static $columnStack = array();
	/**
	 * Columns don't actually render anything, they are
	 * handled at the closing tag of dataTable.
	 */
	static function column( $params, $content, &$smarty, &$repeat ) {
		if( $repeat ) {
			$table = end( self::$dataTableStack );
			$column = new HtmlColumn();
			$table->addColumn( $column );
			array_push( self::$columnStack, $column );
			$html = "";
		} else {
			$table = end( self::$dataTableStack );
			$column = array_pop( self::$columnStack );

			$column->setTemplate( $content );
			$html = "$content";	
		}
		return $html;
	}
	
	static function smarty_componentfaces_radio( $params ) {
		
		$varName = $params["var"];
		$value = $params["value"];
		$label = isset( $params["label"] ) ? $params["label"] : $value;
		$id = isset( $params["id"] ) ? $params["id"] : md5($varName.$value);
		
		
		$fc = PhpFaces_FacesContext::currentInstance();
		$varRef =& $fc->resolve( $varName );
		
		
		
		$name = $fc->getRequestName( $varName );
		$selected = ($varRef == $value);
	
		return "<label class=\"radio\"><input type=\"radio\" name=\"$name\" value=\"$value\"".($selected?" checked=\"checked\"":"")."/> $label</label>";
		
	}
}

class HtmlColumn {
	private $template;
	
	public function setTemplate( $template ) {
		$this->template = $template;
	}
	
	public function getTemplate() {
		return $this->template;
	}
}

class UIData {
	private $data;
	
	public function setRows( $data ) {
		if( is_array( $data ) ) {
			$data = new ListDataModel( $data );
		}
		$this->data = $data;	
	}
	
	public function getRowData() {
		return $this->data->current();
	}
	
	public function getRows() {
		return $this->data;
	}
	
	public function getRowIndex() {
		return $this->data->key();
	}
	
	public function setRowIndex( $index ) {
		$this->data->seek( $index );
	}
}

class HtmlDataTable extends UIData {
	private $columns;
	
	public function __construct() {
		$this->columns = array();
	}
	
	public function addColumn( HtmlColumn $column ) {
		$this->columns[] = $column;
	}
	
	public function getColumns() {
		return $this->columns;
	}
}


class ListDataModel implements SeekableIterator {
	private $data;
	private $valid = false;
	
	/* ============================================
	 * 		Implementing javax.faces.model.ListDataModel
	 * ============================================*/
	
	/**
	 * If there is wrappedData available, return the length of the list.
	 */
	public function getRowCount() {
		return count( $this->getWrappedData() );
	}
	
	/**
	 * If row data is available, return the array element at the index specified by rowIndex.
	 */
	public function getRowData() {
		
	}
	
	/**
	 * Return the zero-relative index of the currently selected row.
	 */
	public function getRowIndex() {
		return $this->key();
	}
	
	/**
	 * Return the object representing the data wrapped by this DataModel, if any.
	 */
	public function getWrappedData() {
		return $this->data;	
	}
	
	/**
	 * Return true if there is wrappedData available, and the current value of rowIndex is greater than or equal to zero, and less than the size of the list.
	 */
	public function isRowAvailable() {
		return $this->getWrappedData() != null && $this->valid();
	}
	
	/**
	 * Set the zero-relative index of the currently selected row, or -1 to indicate that we are not positioned on a row.
	 */
	public function setRowIndex() {
	 	
	}
	 /**
	  * Set the object representing the data collection wrapped by this DataModel.
	  */ 
	 public function setWrappedData( $data ) {
		$this->data = $data;
	 }
	/* ============================================
	 * 		ImplementingSeekable Iterator
	 * ============================================*/
	public function seek( $index ) {

		// if we need to go backwards we actually
		// need to reset, because Iterator doesn't have
		// a 'prev' method.
		if( $index < $this->current() ) {
			$steps = $index;
			$this->rewind();
		} else {
			$steps = $index - $this->current();
		}
		
		for( $i = 0; $i < $steps && $this->valid(); $i++ ) {
			$this->next();
		}
		if( !$this->valid() ) {
			throw new OutOfBoundsException("Invalid seek position");
		}
	}
	
	/* ============================================
	 * 		Implementing Iterator
	 * ============================================*/
	public function __construct( $list ) {
		$this->data = $list;	
	}
	
	function rewind() {
		$this->valid = reset( $this->data ) !== false;
	}
	
	function current() {
		return current( $this->data );
	}
	
	function key() {
		return key( $this->data );
	}
	
	function next() {
		$this->valid = next( $this->array() ) !== false;
	}
	
	function valid() {
		return $this->valid;
	}
}

?>